/*
*  http([config])
*       + url 请求地址
*       + method 请求方式  *GET/DELETE/HEAD/OPTIONS/POST/PUT/PATCH
*       + credentials 携带资源凭证  *include/same-origin/omit
*       + headers:null 自定义的请求头信息「格式必须是纯粹对象」
*       + body:null 请求主体信息「只针对于POST系列请求，根据当前服务器要求，如果用户传递的是一个纯粹对象，我们需要把其变为urlencoded格式字符串(设定请求头中的Content-Type)...」
*       + params:null 设定问号传参信息「格式必须是纯粹对象，我们在内部把其拼接到url的末尾」
*       + responseType 预设服务器返回结果的读取方式  *json/text/arrayBuffer/blob
*       + signal 中断请求的信号
*  -----
*  http.get/head/delete/options([url],[config])  预先指定了配置项中的url/method
*  http.post/put/patch([url],[body],[config])  预先指定了配置项中的url/method/body
* */
import _ from "../assets/utils"
import qs from 'qs'
import {message} from "antd";
/* 核心方法 */
const baseUrl = '/api'
const http = function (config) {
    // ...
    // intial config & validate
    config.body = config.data
    if (!_.isPlainObject(config)) config = {};
    config = Object.assign({
        url: "",
        method: "GET",
        credentials: "include",
        headers: null,
        body: null,
        params: null,
        responseType: "json",
        signal: null,
    }, config);// 设置默认值
    if (!config.url) throw new TypeError('url must be required')
    if (!_.isPlainObject(config.headers)) config.headers = {'Content-Type': 'application/json'}
    if (config.params !== null && !_.isPlainObject(config.params)) config.params = null

    // 处理各种细节
    let {url, method, credentials, headers, body, params, responseType, signal} = config
    url = baseUrl + url;
    // 处理问号传参
    if (params) {
        url += `${url.includes("?") ? '&' : '?'}${qs.stringify(params)}`// 'xxx=xxx&xxx=xx'
    }
    // 处理请求主题信息：按照我们后台的要求，如果传递是普通对象，要设置为 urlencode格式
    if (_.isPlainObject(body)) {
        body = qs.stringify(body)
        headers['Content-Type'] = "application/x-www-form-urlencoded"
    }

    // 类似于 axios钟的请求拦截，每一个请求，递给服务器相同的内容处理
    let token = localStorage.getItem('toekn')
    if (token) headers['authorization'] = token

    // 发送请求
    method = method.toUpperCase()
    config = {
        method,
        credentials,
        headers,
        cache: "no-cache",
        signal
    }
    if (/^(POST|PUT|PATCH)$/i.test(method) && body) config.body = body;

    return fetch(url, config)
        .then(response => {
            let {status, statusText} = response
            if (/^(2|3)\d{2}$/.test(status)) {
                // 请求成功：更具预设的方式，获取需要的值
                let result;
                switch (responseType.toLowerCase()) {
                    case 'text':
                        result = response.text();
                        break;
                    case 'arraybuffer':
                        result = response.arrayBuffer();
                        break;
                    case 'blob':
                        result = response.blob();
                        break;
                    default:
                        result = response.json();
                }
                return result;
            }
            // 请求失败：http状态码错误
            return Promise.reject({
                code: -100,
                status,
                statusText
            })
        })
        .catch(reason => {
            // 失败的统一提示
            message.error('当前网络繁忙，请您稍后再试~')
            return Promise.reject(reason);// 统一处理完提示后，在组件中获取到的依然是失败
        });
};

/* 快捷方法 */
["GET", "HEAD", "DELETE", 'OPTIONS'].forEach(item => {
    http[item.toLowerCase()] = function (url, config) {
        if (!_.isPlainObject(config)) config = {}
        config['url'] = url
        config['method'] = item
        return http(config)
    }
});

["POST", "PUT", "PATCH"].forEach(item => {
    http[item.toLowerCase()] = function (url, config) {
        if (!_.isPlainObject(config)) config = {}
        config['url'] = url
        config['method'] = item
        // config['body'] = body
        return http(config)
    }
})


export default http